package models

import (
	"errors"
	"fmt"
	"qysoft/libs/cache"
	"qysoft/libs/encrypt"
	"qysoft/libs/params"
	"reflect"
	"strconv"
	"strings"
	"time"

	"github.com/astaxie/beego"
	"github.com/astaxie/beego/orm"
)

type Resource struct {
	Id       string    `orm:"column(id);pk"`
	Pid      string    `orm:"column(pid);null"`
	Name     string    `orm:"column(name);null"`
	Logo     string    `orm:"column(logo);null"`
	Url      string    `orm:"column(url);null"`
	Type     int       `orm:"column(type)"`
	Level    int       `orm:"column(level)"`
	Seq      int       `orm:"column(seq)"`
	Hiden    bool      `orm:"column(hiden)"`
	Remark   string    `orm:"column(remark);null"`
	Status   int       `orm:"column(status)"`
	Isdelete int       `orm:"column(isdelete)"`
	Ctime    time.Time `orm:"column(ctime);type(timestamp with time zone)"`
	Creater  string    `orm:"column(creater)"`
	Utime    time.Time `orm:"column(utime);type(timestamp with time zone)"`
	Updater  string    `orm:"column(updater)"`
	Role     []*Role   `orm:"reverse(many)"`
}

func (t *Resource) TableName() string {
	return "sys_resource"
}

func init() {
	orm.RegisterModel(new(Resource))
}

// AddResource insert a new Resource into database and returns
// last inserted Id on success.
func AddResource(m *Resource) (id int64, err error) {
	o := orm.NewOrm()
	id, err = o.Insert(m)
	return
}

// GetResourceById retrieves Resource by Id. Returns error if
// Id doesn't exist
func GetResourceById(id string) (v *Resource, err error) {
	o := orm.NewOrm()
	v = &Resource{Id: id}
	if err = o.Read(v); err == nil {
		return v, nil
	}
	return nil, err
}

//根据Pid 获取Resource
func GetResourceByPid(pid string) ([]Resource, error) {
	o := orm.NewOrm()
	var rList []Resource
	if _, err := o.QueryTable(new(Resource)).Filter("Isdelete", 0).Filter("pid", pid).All(&rList); err == nil {
		return rList, nil
	} else {
		return nil, err
	}
}

// GetAllResource retrieves all Resource matches certain condition. Returns empty list if
// no records exist
func GetAllResource(query map[string]string, fields []string, sortby []string, order []string,
	offset int64, limit int64) (ml []interface{}, err error) {
	o := orm.NewOrm()
	qs := o.QueryTable(new(Resource))
	// query k=v
	for k, v := range query {
		// rewrite dot-notation to Object__Attribute
		k = strings.Replace(k, ".", "__", -1)
		qs = qs.Filter(k, v)
	}
	qs = qs.Filter("Isdelete", 0)
	// order by:
	var sortFields []string
	beego.Debug("	sortby =  )", sortby, order, len(order))
	if len(sortby) != 0 {
		if len(sortby) == len(order) {
			// 1) for each sort field, there is an associated order
			for i, v := range sortby {
				orderby := ""
				if order[i] == "desc" {
					orderby = "-" + v
				} else if order[i] == "asc" {
					orderby = v
				} else {
					return nil, errors.New("Error: Invalid order. Must be either [asc|desc]")
				}
				sortFields = append(sortFields, orderby)
			}
			qs = qs.OrderBy(sortFields...)
		} else if len(sortby) != len(order) && len(order) == 1 {
			// 2) there is exactly one order, all the sorted fields will be sorted by this order
			for _, v := range sortby {
				orderby := ""
				if order[0] == "desc" {
					orderby = "-" + v
				} else if order[0] == "asc" {
					orderby = v
				} else {
					return nil, errors.New("Error: Invalid order. Must be either [asc|desc]")
				}
				sortFields = append(sortFields, orderby)
			}
		} else if len(sortby) != len(order) && len(order) != 1 {
			return nil, errors.New("Error: 'sortby', 'order' sizes mismatch or 'order' size is not 1")
		}
	} else {
		if len(order) != 0 {
			return nil, errors.New("Error: unused 'order' fields")
		}

	}
	sortFields = append(sortFields, "Seq")

	var l []orm.Params
	qs = qs.OrderBy(sortFields...)
	if _, err = qs.Limit(limit, offset).Values(&l, fields...); err == nil {
		if len(fields) == 0 {
			for _, v := range l {
				r, err := GetResourceById(v["Pid"].(string))
				if err == nil && len(r.Name) > 0 {
					v["Pname"] = r.Name
				} else {
					v["Pname"] = "根目录"
				}
				v["Tname"] = params.GetResourceType(int(v["Type"].(int64)))
				ml = append(ml, v)
			}
		} else {
			// trim unused fields
			for _, v := range l {
				m := make(map[string]interface{})
				val := reflect.ValueOf(v)
				for _, fname := range fields {
					m[fname] = val.FieldByName(fname).Interface()
				}
				ml = append(ml, m)
			}
		}
		return ml, nil
	}
	return nil, err
}

// UpdateResource updates Resource by Id and returns error if
// the record to be updated doesn't exist
func UpdateResourceById(m *Resource) (err error) {
	o := orm.NewOrm()
	v := Resource{Id: m.Id}
	// ascertain id exists in the database
	if err = o.Read(&v); err == nil {
		var num int64
		if num, err = o.Update(m); err == nil {
			fmt.Println("Number of records updated in database:", num)
		}
	}
	return
}

// DeleteResource deletes Resource by Id and returns error if
// the record to be deleted doesn't exist
func DeleteResource(id string) (err error) {
	o := orm.NewOrm()
	v := Resource{Id: id}
	// ascertain id exists in the database
	if err = o.Read(&v); err == nil {
		var num int64
		if num, err = o.Delete(&Resource{Id: id}); err == nil {
			fmt.Println("Number of records deleted in database:", num)
		}
	}
	return
}

//SoftDeleteResource 软删除资源数据
//Param id int64
//Param uname string
//return error
func SoftDeleteResource(id string, uname string) (err error) {
	o := orm.NewOrm()
	v := Resource{Id: id}
	if err = o.Read(&v); err == nil {
		v.Isdelete = 1
		v.Updater = uname
		v.Utime = time.Now()
		_, err = o.Update(&v)
	}
	return
}

//获取所有的资源列表
func GetAllResourceList(rtype int64) (count int64, resources []orm.Params) {
	cid := "GetAllResourceList.rtype." + strconv.Itoa(int(rtype))
	cid2 := "GetResourceByRoleId.countd.rtype." + strconv.Itoa(int(rtype))
	err := cache.GetCache(cid, &resources)
	err = cache.GetCache(cid2, &count)
	if err != nil {
		o := orm.NewOrm()
		// resource := Resource{}
		qs := o.QueryTable(new(Resource)).Filter("Status", 1).Filter("Isdelete", 0)
		qs = qs.Limit(-1).OrderBy("Pid", "Seq")
		if rtype > 0 {
			qs = qs.Filter("type", 1)
		}
		count, err = qs.Values(&resources)
		cache_expire, _ := beego.AppConfig.Int("cache_expire")
		cache.SetCache(cid, resources, cache_expire)
		cache.SetCache(cid2, count, cache_expire)
	}

	return count, resources
}

//根据角色获取资源列表
func GetResourceByRoleId(id string, rtype int64) ([]orm.Params, int64) {
	cid := "GetResourceByRoleId.id." + id + "." + strconv.Itoa(int(rtype))
	cid2 := "GetResourceByRoleId.countd." + id + "." + strconv.Itoa(int(rtype))
	var resources []orm.Params
	var count int64
	// tree := new(ResTree)
	beego.Debug(rtype)
	err := cache.GetCache(cid2, &count)
	err = cache.GetCache(cid, &resources)
	if err != nil {
		o := orm.NewOrm()
		resource := new(Resource)
		var reslist orm.ParamsList
		count, _ = o.QueryTable("sys_role_sys_resources").Filter("sys_role_id", id).Limit(-1).ValuesFlat(&reslist, "sys_resource_id")
		qs := o.QueryTable(resource).Filter("id__in", reslist).Filter("isdelete", 0).Filter("Status", 1).OrderBy("Pid", "Seq").Limit(-1)
		if rtype > 0 {
			qs = qs.Filter("type", 1)
		}
		count, _ = qs.Values(&resources)
		cache_expire, _ := beego.AppConfig.Int("cache_expire")
		cache.SetCache(cid, resources, cache_expire)
		cache.SetCache(cid2, count, cache_expire)
	}
	return resources, count
}

//获取资源的子资源
func GetResSubTree(pid string, status int64) ([]orm.Params, error) {
	o := orm.NewOrm()
	resource := new(Resource)
	var resources []orm.Params
	_, err := o.QueryTable(resource).Filter("Pid", pid).Filter("status", status).Filter("isdelete", 0).OrderBy("Pid", "Seq").Values(&resources)
	if err != nil {
		return resources, err
	}
	return resources, nil
}

//GetResMenu 获取目录根资源
func GetResMenu() (resources []Resource, err error) {
	o := orm.NewOrm()
	resource := new(Resource)
	qs := o.QueryTable(resource).Filter("type", 1).Filter("pid", 0).Filter("status", 1)
	_, err = qs.Filter("isdelete", 0).OrderBy("Pid", "Seq").Limit(-1).All(&resources)

	return
}

//GetResMenuNode获取目录根资源
func GetResMenuNode() (resources []Resource, err error) {
	o := orm.NewOrm()
	resource := new(Resource)
	qs := o.QueryTable(resource).Filter("type", 1).Exclude("pid", 0).Filter("status", 1)
	_, err = qs.Filter("isdelete", 0).OrderBy("Pid", "Seq").Limit(-1).All(&resources)
	return
}

//GetResIDByRoleId 根据角色获取资源列表id
func GetResIdByRoleId(id string) ([]string, error) {
	cid := "GetResIdByRoleId.id." + id
	var resIds []string
	err := cache.GetCache(cid, &resIds)
	if err != nil {
		o := orm.NewOrm()
		var reslist orm.ParamsList
		_, err = o.QueryTable("sys_role_sys_resources").Filter("sys_role_id", id).Limit(-1).ValuesFlat(&reslist, "sys_resource_id")
		if err != nil {
			return nil, err
		}
		resIds = nil
		for _, v := range reslist {
			resIds = append(resIds, v.(string))
		}
		cache_expire, _ := beego.AppConfig.Int("cache_expire")
		cache.SetCache(cid, resIds, cache_expire)
	}
	return resIds, err
}

//AddResOpe 增加资源操作
func AddResOpe() (err error) {
	res, _ := GetResMenuNode()
	for _, k := range res {
		var nr, nr1, nr2, nr3, nr4, nr5 Resource
		nr.Id = encrypt.GetGuid()
		nr.Pid = k.Id
		nr.Type = 2
		nr.Seq = 100
		nr.Ctime = time.Now()
		nr.Creater = "admin"
		nr.Updater = "admin"
		nr.Utime = time.Now()
		nr.Name = k.Name + "-查看"

		nr1.Id = encrypt.GetGuid()
		nr1.Pid = k.Id
		nr1.Type = 2
		nr1.Seq = 101
		nr1.Ctime = time.Now()
		nr1.Creater = "admin"
		nr1.Updater = "admin"
		nr1.Utime = time.Now()
		nr1.Name = k.Name + "-增加"

		nr2.Id = encrypt.GetGuid()
		nr2.Pid = k.Id
		nr2.Type = 2
		nr2.Seq = 102
		nr2.Ctime = time.Now()
		nr2.Creater = "admin"
		nr2.Updater = "admin"
		nr2.Utime = time.Now()
		nr2.Name = k.Name + "-删除"

		nr3.Id = encrypt.GetGuid()
		nr3.Pid = k.Id
		nr3.Type = 2
		nr3.Seq = 103
		nr3.Ctime = time.Now()
		nr3.Creater = "admin"
		nr3.Updater = "admin"
		nr3.Utime = time.Now()
		nr3.Name = k.Name + "-修改"

		nr4.Id = encrypt.GetGuid()
		nr4.Pid = k.Id
		nr4.Type = 2
		nr4.Seq = 104
		nr4.Ctime = time.Now()
		nr4.Creater = "admin"
		nr4.Updater = "admin"
		nr4.Utime = time.Now()
		nr4.Name = k.Name + "-分配"

		nr5.Id = encrypt.GetGuid()
		nr5.Pid = k.Id
		nr5.Type = 2
		nr5.Seq = 105
		nr5.Ctime = time.Now()
		nr5.Creater = "admin"
		nr5.Updater = "admin"
		nr5.Utime = time.Now()
		nr5.Name = k.Name + "-审批"
		AddResource(&nr)
		AddResource(&nr1)
		AddResource(&nr2)
		AddResource(&nr3)
		AddResource(&nr4)
		AddResource(&nr5)
	}
	return
}

//Desc 获取资源的树
//Param pid int64 父资源id，一般取0为根路径
//Param status int64 状态
//return tree []map[string]interface{}, err error
func GetResTree(pid string, status int64) (tree []map[string]interface{}, err error) {
	//	var resources []orm.Params
	resources, err := GetResSubTree(pid, status)
	res := make([]map[string]interface{}, 100)
	if err != nil {
		//		for k, v := range resources {
		//			tree[k] = v
		return
		//		}
	} else {
		for k, v := range resources {
			children, er := GetResTree(v["Id"].(string), status)
			v["children"] = children
			res[k] = v
			tree = res
			if er != nil {
				err = er
				return
			}
		}
		return
	}

}

//用户授权角色
func AddRoleResource(roleid string, resid string) (int64, error) {
	o := orm.NewOrm()
	role := Role{Id: roleid}
	res := Resource{Id: resid}
	m2m := o.QueryM2M(&res, "Role")
	num, err := m2m.Add(&role)
	cid := "GetAllResByRoleId.id." + roleid
	cache.DelCache(cid)
	return num, err
}
